package pkg.dao;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.EntityManager;
import pkg.dao.annotations.DontSync;

/**
 *
 * @author willh
 */
public class SyncUtil {
    
    private static SyncUtil instance = new SyncUtil();
    private Map<Class, List<Method>> methodCache = new ConcurrentHashMap<Class, List<Method>>();
    
    /** Creates a new instance of SyncUtil */
    public SyncUtil() {
    }
    
    public static <T> void sync(EntityManager em, T newObj, T oldObj) {
        instance.internalSync(em, newObj, oldObj);
    }
    
    private <T> void internalSync(EntityManager em, T newObj, T oldObj) {
        List<Method> methods = methodCache.get(newObj.getClass());
        if (methods == null) {
            methods = loadMethods(newObj);
        }
        Object emptyArgs[] = new Object[0];
        for (Method m : methods) {
            try {
                Collection newCol = (Collection)m.invoke(newObj, emptyArgs);
                Collection oldCol = (Collection)m.invoke(oldObj, emptyArgs);
                syncCollections(em, oldCol, newCol);
            } catch (IllegalAccessException iae) {
            } catch (InvocationTargetException ite) {
                /** TODO  By Emad
                 * Had to add this ValidationException because we get this condition when we try to traverse a relationship without a session
                 *  Exception Description: An attempt was made to traverse a relationship using indirection that had a null Session.
                 *  This often occurs when an entity with an uninstantiated LAZY relationship is serialized and that lazy relationship is traversed after serialization.
                 *  To avoid this issue, instantiate the LAZY relationship prior to serialization.
                 *
                 * We may have to fix this if this presents a problem with relationship propagation, down the road.  Its too
                 * early to tell now
                 */
            }
        }
        em.flush();
    }
    
    public static void syncCollections(EntityManager em, Collection oldCol, Collection newCol) {
        for(Object o : oldCol) {
            if (newCol == null || !newCol.contains(o)) {
                em.remove(o);
            }
        }
    }
    
    private List<Method> loadMethods(Object newObj) {
        Class c = newObj.getClass();
        Method methods[] = c.getMethods();
        List<Method> collectionMethods = new ArrayList<Method>();
        for(Method m : methods) {
            Class returnType = m.getReturnType();
            boolean flag = m.isAnnotationPresent(DontSync.class);
            if(Collection.class.isAssignableFrom(returnType) && m.getName().startsWith("get") && m.getParameterTypes().length == 0 && !flag) {
                collectionMethods.add(m);
            }
        }
        methodCache.put(newObj.getClass(), collectionMethods);
        return collectionMethods;
    }
}
